package com.gewu.advertisement.core.common

import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.File
import java.io.FileOutputStream
import java.io.IOException

/**
 * @author htt 2020/9/23
 */
private const val TAG = "Downloader"

@Suppress("BlockingMethodInNonBlockingContext")
internal class Downloader : CoroutineScope by SupervisorLogScope(TAG, Dispatchers.IO) {
    private val downloadJobs = mutableMapOf<String, Deferred<File>>()
    private val mutex = Mutex()

    /**
     * 如果文件已存在 就直接返回文件
     * 如果文件正在下载中 那么挂起等待文件下载完毕后返回
     * 否则开始加载文件 挂起等待文件加载完成后返回
     */
    suspend fun download(
        url: String,
        fileName: String,
        downloadPath: String,
        md5: String
    ): File {
        var deferred = mutex.withLock {
            downloadJobs[url]
        }
        //任务已经在进行中了
        if (deferred != null) {
            return deferred.await()
        }
        val file = File(downloadPath, fileName)
        deferred = mutex.withLock {
            if (!file.exists()) {
                //文件不存在 开始下载 然后校验md5
                val def = async {
                    doDownFile(url, file, md5)
                    checkMd5(file, md5)
                    file
                }
                downloadJobs[url] = def
                def
            } else {
                //文件已存在 校验md5
                checkMd5(file, md5)
                return file
            }
        }
        try {
            return deferred!!.await();
        } catch (e: Exception) {
            throw e
        } finally {
            mutex.withLock {
                downloadJobs.remove(url)
            }
        }
    }

    private suspend fun doDownFile(url: String, file: File, md5: String): File {
        Log.i(TAG, "开始下载资源 url:$url  fileName:${file.name}");
        HttpClient.getStream(url) {
            val parentFile = file.parentFile!!
            if (!parentFile.exists()) parentFile.mkdirs()
            FileOutputStream(file, false).use { fileOutput ->
                it.copyTo(fileOutput)
            }
        }
        Log.i(TAG, "资源下载完成 url:$url fileName:${file.name}");
        return file
    }

    private fun checkMd5(file: File, md5: String) {
        val fileMd5 = FileUtil.getFileMD5ToString(file)
        if (md5.isNotEmpty() && !md5.equals(fileMd5, true)) {
            file.delete();
            throw IOException("文件MD5不一致 fileMd5:$fileMd5 md5:$md5")
        }
    }
}